#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

/*
日志器模块实现
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "util.hpp"
#include "level.hpp"
#include "message.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <thread>
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <unordered_map>
namespace PRJLOG
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name,
               LogLevel::value level,
               Formatter::ptr fmt,
               std::vector<LogSink::ptr> sink) : _logger_name(logger_name), _limit_level(level), _fmt(fmt), _sink(sink.begin(), sink.end()) {}
        const std::string &name()
        {
            return _logger_name;
        }
        // 构造日志消息对象并格式化，得到格式化后的日志消息字符串，并进行落地输出
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过输入的参数构造格式化日志消息，并进行落地输出
            // 1.判断当前的日志是否达到日志输出等级
            if (LogLevel::value::DEBUG < _limit_level)
            {
                return;
            }
            // 2.获取日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            // 3.构造logmsg对象
            message msg(LogLevel::value::DEBUG, file, line, res, _logger_name);
            // 4.格式化logmsg对象，获取格式化字符串
            std::stringstream ss;
            _fmt->format(ss, msg);
            // 5.进行落地输出
            log(ss.str().c_str(), ss.str().size());
            va_end(ap);
        }
        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过输入的参数构造格式化日志消息，并进行落地输出
            // 1.判断当前的日志是否达到日志输出等级
            if (LogLevel::value::INFO < _limit_level)
            {
                return;
            }
            // 2.获取日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            // 3.构造logmsg对象
            message msg(LogLevel::value::INFO, file, line, res, _logger_name);
            // 4.格式化logmsg对象，获取格式化字符串
            std::stringstream ss;
            _fmt->format(ss, msg);
            // 5.进行落地输出
            log(ss.str().c_str(), ss.str().size());
            va_end(ap);
        }
        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过输入的参数构造格式化日志消息，并进行落地输出
            // 1.判断当前的日志是否达到日志输出等级
            if (LogLevel::value::WARN < _limit_level)
            {
                return;
            }
            // 2.获取日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            // 3.构造logmsg对象
            message msg(LogLevel::value::WARN, file, line, res, _logger_name);
            // 4.格式化logmsg对象，获取格式化字符串
            std::stringstream ss;
            _fmt->format(ss, msg);
            // 5.进行落地输出
            log(ss.str().c_str(), ss.str().size());
            va_end(ap);
        }
        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过输入的参数构造格式化日志消息，并进行落地输出
            // 1.判断当前的日志是否达到日志输出等级
            if (LogLevel::value::ERROR < _limit_level)
            {
                return;
            }
            // 2.获取日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            // 3.构造logmsg对象
            message msg(LogLevel::value::ERROR, file, line, res, _logger_name);
            // 4.格式化logmsg对象，获取格式化字符串
            std::stringstream ss;
            _fmt->format(ss, msg);
            // 5.进行落地输出
            log(ss.str().c_str(), ss.str().size());
            va_end(ap);
        }
        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过输入的参数构造格式化日志消息，并进行落地输出
            // 1.判断当前的日志是否达到日志输出等级
            if (LogLevel::value::FATAL < _limit_level)
            {
                return;
            }
            // 2.获取日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            // 3.构造logmsg对象
            message msg(LogLevel::value::FATAL, file, line, res, _logger_name);
            // 4.格式化logmsg对象，获取格式化字符串
            std::stringstream ss;
            _fmt->format(ss, msg);
            // 5.进行落地输出
            log(ss.str().c_str(), ss.str().size());
            va_end(ap);
        }

    protected:
        // 不同的日志器有不同的落地方式
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;                         // 互斥锁
        std::string _logger_name;                  // 日志器名称
        std::atomic<LogLevel::value> _limit_level; // 日志器输出日志消息限制等级
        Formatter::ptr _fmt;                       // 日志消息输出格式对象
        std::vector<LogSink::ptr> _sink;           // 日志消息落地数组对象
    };

    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name,
                   LogLevel::value level,
                   Formatter::ptr fmt,
                   std::vector<LogSink::ptr> sink) : Logger(logger_name, level, fmt, sink) {}

    private:
        void log(const char *data, size_t len) override
        {
            std::unique_lock<std::mutex> mutex(_mutex);
            if (_sink.empty())
            {
                return;
            }
            for (auto &sink : _sink)
            {
                sink->Log(data, len);
            }
        }
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name,
                    LogLevel::value level,
                    Formatter::ptr fmt,
                    std::vector<LogSink::ptr> sink,
                    Asyncloopertype loopertype = Asyncloopertype::ASYNC_SAFE)
            : Logger(logger_name, level, fmt, sink), _looper(std::make_shared<Asynclooper>(std::bind(&AsyncLogger::reallog, this, std::placeholders::_1), loopertype)) {}

    private:
        // 负责将日志写到缓冲区中
        void log(const char *data, size_t len) override
        {
            _looper->push(data, len);
        }
        // 负责实际的日志落地
        void reallog(Buffer &buf)
        {
            if (_sink.empty())
                return;
            for (auto sink : _sink)
            {
                sink->Log(buf.begin(), buf.readerablesize());
            }
        }

    private:
        Asynclooper::ptr _looper;
    };

    enum class LoggerType
    {
        Logger_Sync,
        Logger_Async
    };
    /*使用建造者模式建造日志器，而不是让用户直接建造，简化用户的使用复杂程度
        1.抽象出日志器建造者类*/
    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::Logger_Sync),
              _limit_level(LogLevel::value::DEBUG),
              _loopertype(Asyncloopertype::ASYNC_SAFE) {}
        void BuildLoggerType(LoggerType type) { _logger_type = type; }
        void BuildEnableUnsafe() { _loopertype = Asyncloopertype::ASYNC_UNSAFE; }
        void BuildLoggername(const std::string &name) { _logger_name = name; }
        void BuildLoggerLevel(LogLevel::value level) { _limit_level = level; }
        void BuildFormatter(const std::string &fmt)
        {
            _fmt = std::make_shared<Formatter>(fmt);
        }
        template <typename SinkType, typename... ARGS>
        void BuildSink(ARGS &&...args)
        {
            LogSink::ptr sink = std::make_shared<SinkType>(std::forward<ARGS>(args)...);
            _sink.push_back(sink);
        }
        virtual Logger::ptr Builder() = 0;

    protected:
        Asyncloopertype _loopertype;
        LoggerType _logger_type;         // 选择日志器为同步日志器还是异步日志器
        std::string _logger_name;        // 日志器名称
        LogLevel::value _limit_level;    // 日志器限制等级
        Formatter::ptr _fmt;             // 日志消息格式
        std::vector<LogSink::ptr> _sink; // 日志消息落地方式
    };
    /*2.派生出具体的日志器建造者类，分为局部建造者类和全局建造者类
     */
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr Builder() override
        {
            assert(!_logger_name.empty()); // 一定要有日志器名称
            if (_fmt.get() == nullptr)
            {
                _fmt = std::make_shared<Formatter>();
            }
            if (_sink.empty())
            {
                BuildSink<StdoutSink>();
            }
            if (_logger_type == LoggerType::Logger_Async)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _fmt, _sink, _loopertype);
            }
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _fmt, _sink);
        }
    };
    class loggermanagers
    {
    public:
        static loggermanagers &GetInstance()
        {
            static loggermanagers _eton;
            return _eton;
        }
        bool haslogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return false;
            }
            return true;
        }
        void addlogger(Logger::ptr &logger)
        {
            if (haslogger(logger->name()))
            return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        Logger::ptr getlogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return Logger::ptr();
            }
            return it->second;
        }
        Logger::ptr rootlogger()
        {
            return _root_logger;
        }

    private:
        loggermanagers()
        {
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            builder->BuildLoggername("root");
            _root_logger = builder->Builder();
            _loggers.insert(std::make_pair(_root_logger->name(), _root_logger));
        }
        loggermanagers(const loggermanagers &) = delete;
        loggermanagers &operator=(const loggermanagers &) = delete;

    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;                              // 默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers; // 日志器数组
    };
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr Builder() override
        {
            assert(!_logger_name.empty()); // 一定要有日志器名称
            if (_fmt.get() == nullptr)
            {
                _fmt = std::make_shared<Formatter>();
            }
            if (_sink.empty())
            {
                BuildSink<StdoutSink>();
            }
            Logger::ptr logger;
            if (_logger_type == LoggerType::Logger_Async)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _fmt, _sink, _loopertype);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _fmt, _sink);
            }
            loggermanagers::GetInstance().addlogger(logger);
            return logger;
        }
    };
}

#endif